package cn.gyn.gynrpc.demo.consumer;

import cn.gyn.gynrpc.core.annotation.GynConsumer;
import cn.gyn.gynrpc.core.api.Router;
import cn.gyn.gynrpc.core.api.RpcContext;
import cn.gyn.gynrpc.core.cluster.GrayRouter;
import cn.gyn.gynrpc.core.config.ConsumerConfig;
import cn.gyn.gynrpc.demo.api.User;
import cn.gyn.gynrpc.demo.api.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 在这里实现调用远程服务的功能(调用provider里的userService的实现：通过provider里的userService再去调用provider里的真实服务)
 */
@SpringBootApplication
@RestController
//需要添加这个进行spring bean注入配置
@Import({ConsumerConfig.class})
public class GynrpcDemoConsumerApplication {

    @Autowired
    ApplicationContext context;
    /**
     * 这个注解是在field上，要想通过stub拿到并赋值给有注解的field上，有几种常用方法：
     * 1、在spring上下文都扫描完了之后，再去扫描这个注解下的field
     * 2、创建一个类似provider的bootstrap写init方法，然后在consumer里创建ApplicationRunner（等spring都初始化好了之后才会进到这里）
     * 3、还有一个spring声明周期里的方法可以使用：InstantiationAwareBeanPostProcessor（用来处理Bean里的属性的方法），把userService当成是GynrpcDemoConsumerApplication里的一个属性去判断处理
     */
    @GynConsumer
    UserService userService;
//    @GynConsumer
//    OrderService orderService;

    @Autowired
    Demo2 demo2;


    /**
     * 新增rest入口请求，这里实现类似网关的东西，调用方法里去控制负载均衡
     * RequestMapping可以用get调用，PostMapping需要Post调用
     * @param id
     * @return
     */
    @RequestMapping("/")
    public User findBy(@RequestParam("pid") int id) {
        return userService.findById(id);
    }

    @RequestMapping("/find/")
    public User find(@RequestParam("timeout") int timeout) {
        return userService.find(timeout);
    }

    //这里不能用GrayRouter直接声明，是因为在config里声明的时候是声明的Router
    @Autowired
    Router router;

    @RequestMapping("/gray/")
    public String gray(@RequestParam("ratio") int ratio) {
        //这里的写法有点意思！第一次见。把router实例强行转换为GrayRouter，并赋值
        ((GrayRouter) router).setGrayRatio(ratio);
        return "OK-new gray ratio is" + ratio;
    }

    public static void main(String[] args) {
        SpringApplication.run(GynrpcDemoConsumerApplication.class, args);
    }

    /**
     * 执行过程中因为没给ConsumerConfig里的ApplicationRunner提高执行优先级，就先执行了这里的，导致一个加载不成功后就失败了
     * @return
     */
    @Bean
    public ApplicationRunner comsumer_runner() {
        return x -> {
            /*long start = System.currentTimeMillis();
            userService.find(1000);
            System.out.println("userService.find take " +
                    (System.currentTimeMillis() - start) + " ms");*/
            //testAll();
        };
    }

    private void testAll() {
        // 常规int类型，返回User对象
        System.out.println("Case 1. >>===[常规int类型，返回User对象]===");
        User user = userService.findById(1);
        System.out.println("RPC result userService.findById(1) = " + user);

        // 测试方法重载，同名方法，参数不同
        System.out.println("Case 2. >>===[测试方法重载，同名方法，参数不同===");
        User user1 = userService.findById(1, "hubao");
        System.out.println("RPC result userService.findById(1, \"hubao\") = " + user1);

        // 测试返回字符串
        System.out.println("Case 3. >>===[测试返回字符串]===");
        System.out.println("userService.getName() = " + userService.getName());

        // 测试重载方法返回字符串
        System.out.println("Case 4. >>===[测试重载方法返回字符串]===");
        System.out.println("userService.getName(123) = " + userService.getName(123));

        // 测试local toString方法
        System.out.println("Case 5. >>===[测试local toString方法]===");
        System.out.println("userService.toString() = " + userService.toString());

        // 测试long类型
        System.out.println("Case 6. >>===[常规int类型，返回User对象]===");
        System.out.println("userService.getId(10) = " + userService.getId(10));

        // 测试long+float类型
        System.out.println("Case 7. >>===[测试long+float类型]===");
        System.out.println("userService.getId(10f) = " + userService.getId(10f));

        // 测试参数是User类型
        System.out.println("Case 8. >>===[测试参数是User类型]===");
        System.out.println("userService.getId(new User(100,\"KK\")) = " +
                userService.getId(new User(100, "KK")));


        System.out.println("Case 9. >>===[测试返回long[]]===");
        System.out.println(" ===> userService.getLongIds(): ");
        for (long id : userService.getLongIds()) {
            System.out.println(id);
        }

        System.out.println("Case 10. >>===[测试参数和返回值都是long[]]===");
        System.out.println(" ===> userService.getLongIds(): ");
        for (long id : userService.getIds(new int[]{4, 5, 6})) {
            System.out.println(id);
        }

        // 测试参数和返回值都是List类型
        System.out.println("Case 11. >>===[测试参数和返回值都是List类型]===");
        List<User> list = userService.getList(List.of(
                new User(100, "KK100"),
                new User(101, "KK101")));
        list.forEach(System.out::println);

        // 测试参数和返回值都是Map类型
        System.out.println("Case 12. >>===[测试参数和返回值都是Map类型]===");
        Map<String, User> map = new HashMap<>();
        map.put("A200", new User(200, "KK200"));
        map.put("A201", new User(201, "KK201"));
        userService.getMap(map).forEach(
                (k, v) -> System.out.println(k + " -> " + v)
        );

        System.out.println("Case 13. >>===[测试参数和返回值都是Boolean/boolean类型]===");
        System.out.println("userService.getFlag(false) = " + userService.getFlag(false));
        System.out.println("Case 14. >>===[测试参数和返回值都是User[]类型]===");
        User[] users = new User[]{
                new User(100, "KK100"),
                new User(101, "KK101")};
        Arrays.stream(userService.findUsers(users)).forEach(System.out::println);

        System.out.println("Case 15. >>===[测试参数为long，返回值是User类型]===");
        User userLong = userService.findById(10000L);
        System.out.println(userLong);

        System.out.println("Case 16. >>===[测试参数为boolean，返回值都是User类型]===");
        User user100 = userService.ex(false);
        System.out.println(user100);

        System.out.println("Case 17. >>===[测试服务端抛出一个RuntimeException异常]===");
        try {
            User userEx = userService.ex(true);
            System.out.println(userEx);
        } catch (RuntimeException e) {
            System.out.println(" ===> exception: " + e.getMessage());
        }

        System.out.println("Case 18. >>===[测试服务端抛出一个超时重试后成功的场景]===");
        // 超时设置的【漏斗原则】
        // A 2000 -> B 1500 -> C 1200 -> D 1000
        long start = System.currentTimeMillis();
        userService.find(1100);
        userService.find(1100);
        System.out.println("userService.find take "
                + (System.currentTimeMillis() - start) + " ms");

        System.out.println("Case 19. >>===[测试通过Context跨消费者和提供者进行传参]===");
        String Key_Version = "rpc.version";
        String Key_Message = "rpc.message";
        RpcContext.setContextParameter(Key_Version, "v8");
        RpcContext.setContextParameter(Key_Message, "this is a test message");
        RpcContext.setContextParameter("a", "a100");
        RpcContext.setContextParameter("b", "b100");
        String version = userService.echoParameter(Key_Version);
        String message = userService.echoParameter(Key_Message);
        System.out.println(" ===> echo parameter from c->p->c: " + Key_Version + " -> " + version);
        System.out.println(" ===> echo parameter from c->p->c: " + Key_Message + " -> " + message);
        //参数用完要及时清空，否则会导致上下文污染：当前线程被复用，第二次处理其他请求，带了上一次的上下文参数。
        //这里的清空原本应该写在ParameterFilter里的postfilter方法里的，在consumerRPC调用并返回了结果后，会进入该方法清空上下文参数。
        RpcContext.ContextParameters.get().clear();
    }


}
